/*
 * Copyright 2016 - 2018 NXP
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * o Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * o Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * o Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*!
 * @file bcc.c
 *
 * Battery cell controller SW driver.
 * Supports boards based on MC33771C.
 *
 * This module is common for all supported models.
 */

/*******************************************************************************
 * Includes
 ******************************************************************************/
#include "../MC3377X_USER.h"
#ifdef MC33771C
#include "mc33771c.h"
#include "mc33771c_communication.h"
#include "mc33771c_peripheries.h"
#include "../include/includes.h"
#include <math.h>
//#include "../src/Task/Task.h"
/*******************************************************************************
 * Definitions
 ******************************************************************************/

/*! @brief Cell map when 7 cells are connected to MC33771. */
#define BCC_CM_MC33771_7CELLS     0x380FU

/* NTC configuration and minimal & maximal values. */
/*! @brief Size of NTC look-up table. */
#define BCC_NTC_TABLE_SIZE        (BCC_NTC_MAXTEMP - BCC_NTC_MINTEMP + 1)
/*! @brief 0 degree Celsius converted to Kelvin. */
#define BCC_NTC_DEGC_0            273.15
/*! @brief Maximal voltage (5V). */
#define BCC_NTC_VCOM              5.0
/*! @brief Resolution of measured voltage in Volts (U = 152.58789 uV *
 *  register_value), with 5V maximal voltage. */
#define BCC_NTC_REGISTER_RES      0.00015258789

/** Diagnostics time constants. **/
/*!
 * @brief Measurement time constant tau in [us].
 *
 * See formula in MC33771C datasheet.
 *
 * @param drvConfig Pointer to driver instance configuration.
 * @return Tau constant in [us].
 */
#define BCC_DIAG_TAU(drvConfig) ( \
    (drvConfig->compConfig.ctFilterComp->rLpf1 *       \
     drvConfig->compConfig.ctFilterComp->cLpf) / 1000U \
)

/*!
 * @brief Measurement time constant tau_I in [us].
 *
 * See formula in MC33771C datasheet.
 *
 * @param drvConfig Pointer to driver instance configuration.
 * @return Tau_I constant in [us].
 */
#define BCC_DIAG_TAU_I(drvConfig) ( \
    (drvConfig->compConfig.isenseComp.rLpfi *              \
     (drvConfig->compConfig.isenseComp.cLpfi +             \
      (2U * drvConfig->compConfig.isenseComp.cD))) / 1000U \
)

/*!
 * @brief Diagnostic time t_diag to detect an open from the shunt to the
 * current filter in [us].
 *
 * See formula in MC33771C datasheet.
 *
 * @param drvConfig Pointer to driver instance configuration.
 * @return T_diag constant in [us].
 */
#define BCC_DIAG_T_DIAG(drvConfig) ( \
    ((drvConfig->compConfig.isenseComp.cLpfi +                            \
      drvConfig->compConfig.isenseComp.cD) *                              \
     ((BCC_DIAG_V_ISENSE_OL + ((drvConfig->compConfig.isenseComp.rShunt * \
         drvConfig->compConfig.isenseComp.iMax) / 1000U)) /               \
      (BCC_DIAG_ISENSE_OL))) / 1000U                                      \
)

/*!
 * @brief Returns a non-zero value when desired cell (cellNo) is connected
 * to the BCC specified by CID. Otherwise returns zero.
 *
 * @param drvConfig Pointer to driver instance configuration.
 * @param cid Cluster Identification Address.
 * @param cellNo Number of a cell (range is {1, ..., 14} for MC33771.
 * @return Non-zero value if cell is connected, zero otherwise.
 */
#define BCC_IS_CELL_CONN(drvConfig, cid, cellNo) \
    (drvConfig->drvData.cellMap[(cid) - 1] & (1U << ((cellNo) - 1U)))

/*!
 * @brief Calculates final temperature value.
 *
 * @param tblIdx Index of value in NTC table which is close
 *        to the register value provided by user.
 * @param degTenths Fractional part of temperature value.
 * @return Temperature.
 */
#define BCC_COMP_TEMP(tblIdx, degTenths) \
    ((((tblIdx) + BCC_NTC_MINTEMP) * 10) + (degTenths))

/*!
 * Returns true if value VAL is in the range defined by MIN and MAX values
 * (range includes the border values).
 *
 * @param val Comparison value.
 * @param min Minimal value of the range.
 * @param max Maximal value of the range.
 * @return True if value is the range. False otherwise.
 */
#define BCC_IS_IN_RANGE(val, min, max)   (((val) >= (min)) && ((val) <= (max)))

/*******************************************************************************
 * Global variables (constants)
 ******************************************************************************/

/**
 * NTC look up table intended for resistance to temperature conversion. After
 * driver initialization, array item contains raw value from a register.
 * Index of the item is temperature value.
 */
uint16_t g_ntcTable[BCC_NTC_TABLE_SIZE];

/** Addresses of configurable registers.
 *
 * TH_CT14 - TH_CT7 (0x4c - 0x53) registers. These values are ignored
 * in the initialization. */
static const uint8_t BCC_INIT_CONF_REG_ADDR[BCC_INIT_CONF_REG_CNT] = {
    /* Note: INIT register is initialized automatically. SYS_CFG_GLOBAL register
     *       contains only command GO2SLEEP (no initialization needed).
     *       EEPROM_CTRL, FUSE_MIRROR_DATA and FUSE_MIRROR_CNTL registers are
     *       not initialized. */

    BCC_REG_SYS_CFG1_ADDR,
    BCC_REG_SYS_CFG2_ADDR,
    BCC_REG_ADC_CFG_ADDR,
    BCC_REG_ADC2_OFFSET_COMP_ADDR,
    BCC_REG_OV_UV_EN_ADDR,
	BCC_REG_TPL_CFG_ADDR,
    BCC_REG_GPIO_CFG1_ADDR,
    BCC_REG_GPIO_CFG2_ADDR,
    BCC_REG_FAULT_MASK1_ADDR,
    BCC_REG_FAULT_MASK2_ADDR,
    BCC_REG_FAULT_MASK3_ADDR,
    BCC_REG_WAKEUP_MASK1_ADDR,
    BCC_REG_WAKEUP_MASK2_ADDR,
    BCC_REG_WAKEUP_MASK3_ADDR,
    BCC_REG_TH_ALL_CT_ADDR,
    BCC_REG_TH_CT14_ADDR,
    BCC_REG_TH_CT13_ADDR,
    BCC_REG_TH_CT12_ADDR,
    BCC_REG_TH_CT11_ADDR,
    BCC_REG_TH_CT10_ADDR,
    BCC_REG_TH_CT9_ADDR,
    BCC_REG_TH_CT8_ADDR,
    BCC_REG_TH_CT7_ADDR,
    BCC_REG_TH_CT6_ADDR,
    BCC_REG_TH_CT5_ADDR,
    BCC_REG_TH_CT4_ADDR,
    BCC_REG_TH_CT3_ADDR,
    BCC_REG_TH_CT2_ADDR,
    BCC_REG_TH_CT1_ADDR,
    BCC_REG_TH_AN6_OT_ADDR,
    BCC_REG_TH_AN5_OT_ADDR,
    BCC_REG_TH_AN4_OT_ADDR,
    BCC_REG_TH_AN3_OT_ADDR,
    BCC_REG_TH_AN2_OT_ADDR,
    BCC_REG_TH_AN1_OT_ADDR,
    BCC_REG_TH_AN0_OT_ADDR,
    BCC_REG_TH_AN6_UT_ADDR,
    BCC_REG_TH_AN5_UT_ADDR,
    BCC_REG_TH_AN4_UT_ADDR,
    BCC_REG_TH_AN3_UT_ADDR,
    BCC_REG_TH_AN2_UT_ADDR,
    BCC_REG_TH_AN1_UT_ADDR,
    BCC_REG_TH_AN0_UT_ADDR,
    BCC_REG_TH_ISENSE_OC_ADDR,
    BCC_REG_TH_COULOMB_CNT_MSB_ADDR,
    BCC_REG_TH_COULOMB_CNT_LSB_ADDR,
};


TYPE_BatteryPack BatPack;


static uint16_t BCC_ADC_CONF_DIAG;
/*******************************************************************************
 * Prototypes of internal functions
 ******************************************************************************/

/*!
 * @brief This function calculates the tau_diag time constant.
 *
 * @param drvConfig Pointer to driver instance configuration.
 */
static void BCC_CalcTauDiag(bcc_drv_config_t* const drvConfig);

/*!
 * @brief This function enables MC33664 TPL device. It uses EN and
 * INTB pins.
 *
 * @param drvConfig Pointer to driver instance configuration.
 *
 * @return bcc_status_t Error code.
 */
static bcc_status_t BCC_EnableTPL(bcc_drv_config_t* const drvConfig);


/*!
 * @brief This function initializes a BCC device or all devices in daisy chain.
 *
 * @param drvConfig Pointer to driver instance configuration.
 * @param devConf Initialization values of BCC device registers.
 *                devConf[0][x] belongs to device with CID 1,
 *                devConf[1][x] belongs to device with CID 2, etc.
 *
 * @return bcc_status_t Error code.
 */
static bcc_status_t BCC_InitRegisters(bcc_drv_config_t* const drvConfig,
    const uint16_t devConf[][BCC_INIT_CONF_REG_CNT]);

/*!
 * @brief This function assigns CID to a BCC device that has CID equal to zero.
 * It closes bus switch to allow communication with the next BCC.
 *
 * @param drvConfig Pointer to driver instance configuration.
 * @param cid Cluster ID of BCC device.
 *
 * @return bcc_status_t Error code.
 */
static bcc_status_t BCC_AssignCid(bcc_drv_config_t* const drvConfig,
    bcc_cid_t cid);

/*!
 * @brief This function initializes all connected BCC devices.
 *
 * @param drvConfig Pointer to driver instance configuration.
 * @param devConf Initialization values of BCC device registers.
 *                devConf[0][x] belongs to device with CID 1,
 *                devConf[1][x] belongs to device with CID 2, etc.
 *
 * @return bcc_status_t Error code.
 */
static bcc_status_t BCC_InitDevices(bcc_drv_config_t* const drvConfig,
    const uint16_t devConf[][BCC_INIT_CONF_REG_CNT]);

/* Internal functions used by diagnostics. */
/*!
 * @brief This function enters diagnostic mode.
 *
 * @param drvConfig Pointer to driver instance configuration.
 * @param cid Cluster ID of BCC device.
 *
 * @return bcc_status_t Error code.
 */
static bcc_status_t BCC_EnterDiagnostics(bcc_drv_config_t* const drvConfig,
    bcc_cid_t cid);

/*!
 * @brief This function calculates exits diagnostic mode.
 *
 * @param drvConfig Pointer to driver instance configuration.
 * @param devConf Initialization values of BCC registers.
 *
 * @return bcc_status_t Error code.
 */
static bcc_status_t BCC_ExitDiagnostics(bcc_drv_config_t* const drvConfig,
    bcc_cid_t cid);

/*!
 * @brief This function starts on-demand conversion and waits for completion.
 *
 * @param drvConfig Pointer to driver instance configuration.
 * @param cid Cluster ID of BCC device.
 *
 * @return bcc_status_t Error code.
 */
static bcc_status_t BCC_RunCOD(bcc_drv_config_t* const drvConfig,
    bcc_cid_t cid);

/*!
 * @brief This function commands CT or CB diagnostic switches.
 *
 * @param drvConfig Pointer to driver instance configuration.
 * @param cid Cluster ID of BCC device.
 * @param switches Selection of CT or CB switches.
 * @param odd State of odd diagnostic switches.
 * @param even State of even diagnostic switches.
 *
 * @return bcc_status_t Error code.
 */
static bcc_status_t BCC_CommandSwitches(bcc_drv_config_t* const drvConfig,
    bcc_cid_t cid, bcc_diag_switch_sel_t switches,
    bcc_diag_switch_pos_t odd, bcc_diag_switch_pos_t even);

/*!
 * @brief This function computes voltage in uV from register raw values.
 *
 * @param rawMeas Array containing raw values from registers.
 * @param size Size of the array.
 * @param convMeas Resulting array containing converted values.
 */
static inline void BCC_ConvertVoltageArray(const uint16_t rawMeas[],
    uint8_t size, uint32_t convMeas[]);

/*!
 * @brief This function is part overvoltage and undervoltage functional
 * verification. It commands CT switches and measures voltage on cells.
 *
 * @param drvConfig Pointer to driver instance configuration.
 * @param cid Cluster ID of BCC device.
 * @param odd State of odd switches.
 * @param even State of even switches.
 * @param fltOvrv Result of the function containing CELL_OV_FLT register value.
 * @param fltUndv Result of the function containing CELL_UV_FLT register value.
 *
 * @return bcc_status_t Error code.
 */
static bcc_status_t BCC_DiagOvuvPart(bcc_drv_config_t* const drvConfig,
    bcc_cid_t cid, bcc_diag_switch_pos_t odd, bcc_diag_switch_pos_t even,
    uint16_t *fltOvrv, uint16_t *fltUndv);

/*!
 * @brief This function is part of CTX open detect. It clear OV and UV
 * fault registers, commands CT switches and measures voltage on cells.
 *
 * @param drvConfig Pointer to driver instance configuration.
 * @param cid Cluster ID of BCC device.
 * @param odd State of odd switches.
 * @param even State of even switches.
 * @param measVal Result of the function containing measured values for
 *                CT 14 - 1 (MC33771).
 *
 * @return bcc_status_t Error code.
 */
static bcc_status_t BCC_DiagCtxopenPart(bcc_drv_config_t* const drvConfig,
    bcc_cid_t cid, bcc_diag_switch_pos_t odd, bcc_diag_switch_pos_t even,
    uint16_t *measVal);

/* Internal function used by DiagCurrentMeas function only. */
/*!
 * @brief This function is part of current measurement diagnostics (the first
 * procedure). It resets Coulomb counter, initiates conversion and reads
 * measured current.
 *
 * @param drvConfig Pointer to driver instance configuration.
 * @param cid Cluster ID of BCC device.
 * @param current Result of the diagnostic containing measured ISENSE
 *                voltage in [uV].
 *
 * @return bcc_status_t Error code.
 */
static bcc_status_t BCC_DiagRunImeas(bcc_drv_config_t* const drvConfig,
    bcc_cid_t cid, int32_t *current);

/* Internal function used by DiagGPIOxOtUt function only. */
/*!
 * @brief This function is part of GPIO OT and UT functional verification.
 *
 * Writes value given in parameter to GPIO_CFG2 register, initiates conversion,
 * reads AN_OT_UT_FLT register and clears OT/UT faults.
 *
 * @param drvConfig Pointer to driver instance configuration.
 * @param cid Cluster ID of BCC device.
 * @param gpioCFG2Val Value of GPIO_CFG2 GPIO configuration register.
 * @param otUtStat Over/Under-temperature status for GPIOs (expected for all
 *                 GPIOs). It contains value of AN_OT_UT_FLT register.
 *
 * @return bcc_status_t Error code.
 */
static bcc_status_t BCC_GpioOtUtPart(bcc_drv_config_t* const drvConfig,
    bcc_cid_t cid, uint16_t gpioCFG2Val, uint16_t *otUtStat);

/*******************************************************************************
 * Internal function
 ******************************************************************************/

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_CalcTauDiag
 * Description   : This function calculates the tau_diag time constant.
 *
 *END**************************************************************************/
static void BCC_CalcTauDiag(bcc_drv_config_t* const drvConfig)
{
    double rLpf1 = (double)(drvConfig->compConfig.ctFilterComp->rLpf1);
    double rLpf2 = (double)(drvConfig->compConfig.ctFilterComp->rLpf2);
    double cLpf = ((double)(drvConfig->compConfig.ctFilterComp->cLpf)) / 1000000000;
    double cIn = ((double)(drvConfig->compConfig.ctFilterComp->cIn)) / 1000000000;
    double rPd = (double)(BCC_RPD);
    double tauDiag, c, k;

    c = rPd + 2 * (rLpf1 + rLpf2);
    c = c / (2 * cLpf * cIn * rLpf1 * rLpf2 * rPd);

    k = cLpf * rLpf1 * (rPd + 2 * rLpf2) + 2 * cIn * rPd * (rLpf1 + rLpf2);
    k = k / (4 * cLpf * cIn * rLpf1 * rLpf2 * rPd);

//    tauDiag = k * (1 - sqrt(1 - (c / (k * k))));
    tauDiag = 1556.7300356473388;


    drvConfig->drvData.tauDiagn = (uint16_t)(1000000 / tauDiag);

    tauDiag = (rPd+2*rLpf2)*cLpf/2;
    tauDiag+= (2*rLpf1)*(2*rLpf2+rPd)/(2*rLpf1+2*rLpf2+rPd)*cLpf/2;
    tauDiag+= (rPd*(2*rLpf1+2*rLpf2))/(2*rLpf1+2*rLpf2+rPd)*cIn;
    tauDiag+= ((rLpf1+rLpf2)*(rLpf1+rLpf2+rPd)/(2*rLpf1+2*rLpf2+rPd)+(rLpf1+rLpf2+rPd))*cIn;
    tauDiag+= rPd*cIn;

    drvConfig->drvData.tauDiag = (uint16_t)( tauDiag*1000000 );
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_EnableTPL
 * Description   : This function enables MC33664 TPL device. It uses EN and
 *                 INTB pins.
 *
 *END**************************************************************************/
static bcc_status_t BCC_EnableTPL(bcc_drv_config_t* const drvConfig)
{
    uint32_t timeout = 500; /* Wake-up timeout. */

    /* Set normal state (transition from low to high). */
    BCC_WriteEnPin(drvConfig->drvInstance, 0);
    /* Wait at least 100 us. */
    BCC_WaitUs(150);
    BCC_WriteEnPin(drvConfig->drvInstance, 1);

    /* Note: MC33664 has time tReady (max. 100 us, equal to the LOW level)
     * to take effect.  */
    while ((BCC_ReadIntbPin(drvConfig->drvInstance) > 0) && (timeout > 0))
    {
        /* Wait for INTB transition from high to low (max. 100 us). */
        /* Timeout. */
    	if(timeout%100==0){
			taskDISABLE_INTERRUPTS();
			Feed_Watchdog();
			taskENABLE_INTERRUPTS();
    	}
    	BCC_WaitUs(10);
        timeout--;
    }

    if (timeout == 0)
    {
        return BCC_STATUS_COM_TIMEOUT;
    }

    timeout = 500;
    while ((BCC_ReadIntbPin(drvConfig->drvInstance) == 0) && (timeout > 0))
    {
        /* Wait for INTB transition from low to high (typ. 100 us). */
    	if(timeout%100==0){
			taskDISABLE_INTERRUPTS();
			Feed_Watchdog();
			taskENABLE_INTERRUPTS();
		}
        timeout--;
    }

    /* Now the device should be in normal mode (i.e. after INTB low to high
    * transition). For sure wait for 150 us. */
    BCC_WaitUs(150);

    return (timeout == 0) ? BCC_STATUS_COM_TIMEOUT : BCC_STATUS_SUCCESS;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_InitRegisters
 * Description   : This function initializes a BCC device or all devices in
 *                 daisy chain.
 *
 *END**************************************************************************/
static bcc_status_t BCC_InitRegisters(bcc_drv_config_t* const drvConfig,
    const uint16_t devConf[][BCC_INIT_CONF_REG_CNT])
{
    uint8_t i;
    bcc_status_t error;
    uint16_t regVal = 0U;
    uint16_t cfgPowerPramReg = 0;

    /* Initialize all registers according to according to the user values. */

	for (i = 0; i < 8/*BCC_INIT_CONF_REG_CNT*/; i++)
	{
		error = BCC_WriteRegisterGlobal(drvConfig, BCC_INIT_CONF_REG_ADDR[i], devConf[0][i]);
		if (error != BCC_STATUS_SUCCESS)
		{
			return error;
		}
	}

	BCC_WriteRegister(drvConfig, BCC_CID_DEV1,BCC_REG_GPIO_CFG1_ADDR, 0x0F);
	BCC_WriteRegister(drvConfig, BCC_CID_DEV1,BCC_REG_GPIO_CFG2_ADDR, 0x03);

	// last dev InitReg 01Reg bit6 bit7 must set 1 by
	BCC_ReadRegisters(drvConfig, BCC_CID_DEV1+AFE_USING_CNT-1, BCC_REG_INIT_ADDR, 1U, &regVal);

	cfgPowerPramReg = regVal | 0x80;
	BCC_WriteRegister(drvConfig, BCC_CID_DEV1+AFE_USING_CNT-1, BCC_REG_INIT_ADDR, cfgPowerPramReg);

	for (i = 0; i < 8/*BCC_INIT_CONF_REG_CNT*/; i++)
	{
		error=BCC_ReadRegisters(drvConfig, BCC_CID_DEV1+i, BCC_REG_INIT_ADDR, 1U, &regVal);

		if(error!=BCC_STATUS_SUCCESS)
			return error;
	}

    return error;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_AssignCid
 * Description   : This function assigns CID to a BCC device that has CID equal
 *                 to zero.
 *
 *END**************************************************************************/
static bcc_status_t BCC_AssignCid(bcc_drv_config_t* const drvConfig,
    bcc_cid_t cid)
{
    uint16_t regVal = 0U;   /* Value of a register. */
    bcc_status_t error;

    /* Check if unassigned node replies. This is the first reading after device
     * reset. */
    /*error = BCC_ReadRegisters(drvConfig, BCC_CID_UNASSIG, BCC_REG_INIT_ADDR, 1U,
                &regVal);*/

    /* Note: in SPI communication mode the device responds with all zero and the
     * correct CRC (null response) during the very first message. */
    //if ((error != BCC_STATUS_SUCCESS) && (error != BCC_STATUS_NULL_RESP))
    {
    ////    return error;
    }

    /* Assign CID to be able to initialize next BCC device.
     * Note: It is forbidden to use global write command to assign CID (writing
     * into INIT register). */
    regVal = BCC_SET_CID(regVal, cid);

    error = BCC_WriteRegister(drvConfig, BCC_CID_UNASSIG, BCC_REG_INIT_ADDR, regVal);
    if (error != BCC_STATUS_SUCCESS)
    {
        return error;
    }

    /* Check if assigned node replies. */
#if 0  // do not
    return BCC_ReadRegisters(drvConfig, cid, BCC_REG_INIT_ADDR, 1U, &regVal);
#endif
}



/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_InitDevices
 * Description   : This function initializes all connected BCC devices.
 *
 *END**************************************************************************/
static bcc_status_t BCC_InitDevices(bcc_drv_config_t* const drvConfig,
    const uint16_t devConf[][BCC_INIT_CONF_REG_CNT])
{
    uint8_t i;
    uint16_t regVal; /* Value of a register. */
    bcc_status_t error;

    /* Assign CID and close bus switch of all configured BCC devices. */
    for (i = 0U; i < drvConfig->devicesCnt; i++)
    {
    	__asm volatile ("cpsid i" : : : "memory");//taskDISABLE_INTERRUPTS();
    	BCC_CS2GPIO();
        /* Two consecutive transitions of CSB_TX from low to high. */
        /* CSB_TX low. */
        BCC_WriteCsbPin(drvConfig->drvInstance, 0);
        /* Wait for t1; 20 us < t1 < 24us. */
        //taskDISABLE_INTERRUPTS();
        BCC_WaitUs(22U);
        //taskENABLE_INTERRUPTS();
        /* CSB_TX high. */
        BCC_WriteCsbPin(drvConfig->drvInstance, 1);
        /* Wait for t2; 500 us < t2 < 700μs. */
        //taskDISABLE_INTERRUPTS();
        BCC_WaitUs(600U);
        //taskENABLE_INTERRUPTS();
        /* CSB_TX low. */
        BCC_WriteCsbPin(drvConfig->drvInstance, 0);
        /* Wait for t1; 20 us < t1. */
        //taskDISABLE_INTERRUPTS();
        BCC_WaitUs(22);
        //taskENABLE_INTERRUPTS();
        /* CSB_TX high. */
        BCC_WriteCsbPin(drvConfig->drvInstance, 1);
//        BCC_GPIO2CS();
        /*Sleep mode to normal mode time after TPL bus wake-up: 1ms*/
        BCC_WaitUs(1200U);
        __asm volatile ("cpsie i" : : : "memory");//taskENABLE_INTERRUPTS();

        if ((error = BCC_AssignCid(drvConfig, BCC_CID_DEV1 + i)) != BCC_STATUS_SUCCESS)
        {
            return error;
        }
    }

    /* Initialize registers of device(s). */
    if (devConf != NULL)
    {
        if ((error = BCC_InitRegisters(drvConfig, devConf)) != BCC_STATUS_SUCCESS)
        {
            return error;
        }
    }

    return BCC_STATUS_SUCCESS;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_EnterDiagnostics
 * Description   : This function enters diagnostic mode.
 *
 *END**************************************************************************/
static bcc_status_t BCC_EnterDiagnostics(bcc_drv_config_t* const drvConfig,
    bcc_cid_t cid)
{
    uint16_t retVal;    /* Value of SYS_CFG1 register. */
    bcc_status_t error;

    error = BCC_UpdateRegister(drvConfig, cid, BCC_REG_SYS_CFG1_ADDR,
                BCC_W_GO2DIAG_MASK, BCC_DIAG_MODE_ENABLED);
    if (error != BCC_STATUS_SUCCESS)
    {
        return error;
    }

    /* Check that the device is in diagnostic mode. */
    error = BCC_ReadRegisters(drvConfig, cid, BCC_REG_SYS_CFG1_ADDR, 1U, &retVal);
    if (error != BCC_STATUS_SUCCESS)
    {
        return error;
    }

    return (retVal & BCC_R_DIAG_ST_MASK) ? BCC_STATUS_SUCCESS : BCC_STATUS_DIAG_FAIL;
}  

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_ExitDiagnostics
 * Description   : This function exits diagnostic mode.
 *
 *END**************************************************************************/
static bcc_status_t BCC_ExitDiagnostics(bcc_drv_config_t* const drvConfig,
    bcc_cid_t cid)
{
    return BCC_UpdateRegister(drvConfig, cid, BCC_REG_SYS_CFG1_ADDR,
            BCC_W_GO2DIAG_MASK, BCC_DIAG_MODE_DISABLED);
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_RunCOD
 * Description   : This function starts on-demand conversion and waits for
 *                 completion.
 *
 *END**************************************************************************/
static bcc_status_t BCC_RunCOD(bcc_drv_config_t* const drvConfig, bcc_cid_t cid)
{
    bool complete;      /* Conversion complete flag. */
    bcc_status_t error;

    /*start ADC conversion for diag ADC conversion*/
    error = BCC_WriteRegisterGlobal(drvConfig, BCC_REG_ADC_CFG_ADDR, BCC_ADC_CONF_DIAG);

    if (error != BCC_STATUS_SUCCESS)
    {
        return error;
    }
    BCC_WaitUs(600);
    do
    {
        error = BCC_IsConverting(drvConfig, cid, &complete);
        if (error != BCC_STATUS_SUCCESS)
        {
            return error;
        }
    } while (!complete);

    return BCC_STATUS_SUCCESS;
}
  
/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_CommandSwitches
 * Description   : This function commands CT or CB diagnostic switches.
 *
 *END**************************************************************************/
static bcc_status_t BCC_CommandSwitches(bcc_drv_config_t* const drvConfig,
    bcc_cid_t cid, bcc_diag_switch_sel_t switches, bcc_diag_switch_pos_t odd,
    bcc_diag_switch_pos_t even)
{
    uint16_t mask;     /* Register mask. */
    uint16_t command;  /* Command of diagnostic switches (data field of a frame). */

    /* Create content of a frame to command CB outputs */
    if (switches == BCC_SWITCH_SEL_CT)
    {
        mask = BCC_RW_CT_OL_ODD_MASK | BCC_RW_CT_OL_EVEN_MASK;
        command = (even == BCC_SWITCH_POS_OPEN) ? BCC_CT_OL_EVEN_OPEN : BCC_CT_OL_EVEN_CLOSED;
        command |= (odd == BCC_SWITCH_POS_OPEN) ? BCC_CT_OL_ODD_OPEN : BCC_CT_OL_ODD_CLOSED;
    }
    else
    {
        mask = BCC_RW_CB_OL_ODD_MASK | BCC_RW_CB_OL_EVEN_MASK;
        command = (even == BCC_SWITCH_POS_OPEN) ? BCC_CB_OL_EVEN_OPEN : BCC_CB_OL_EVEN_CLOSED;
        command |= (odd == BCC_SWITCH_POS_OPEN) ? BCC_CB_OL_ODD_OPEN : BCC_CB_OL_ODD_CLOSED;
    }

    return BCC_UpdateRegister(drvConfig, cid, BCC_REG_SYS_DIAG_ADDR, mask, command);
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_ConvertVoltageArray
 * Description   : This function computes voltage in uV from register raw values.
 *
 *END**************************************************************************/
static inline void BCC_ConvertVoltageArray(const uint16_t rawMeas[],
    uint8_t size, uint32_t convMeas[])
{
    uint8_t i;

    for (i = 0U; i < size; i++)
    {
        convMeas[i] = BCC_GET_VOLT(rawMeas[i]);
    }
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_DiagOvuvPart
 * Description   : This function is part overvoltage and undervoltage functional
 *                 verification. It commands CT switches and measures voltage on
 *                 cells.
 *
 *END**************************************************************************/
static bcc_status_t BCC_DiagOvuvPart(bcc_drv_config_t* const drvConfig,
    bcc_cid_t cid, bcc_diag_switch_pos_t odd, bcc_diag_switch_pos_t even,
    uint16_t *fltOvrv, uint16_t *fltUndv)
{
    uint16_t fault[2];  /* Value of OV and UV fault registers. */
    bcc_status_t error;

    /* Clear OV, UV faults. */
    error = BCC_WriteRegister(drvConfig, cid, BCC_REG_CELL_OV_FLT_ADDR, 0x0000U);
    error |= BCC_WriteRegister(drvConfig, cid, BCC_REG_CELL_UV_FLT_ADDR, 0x0000U);
    if (error != BCC_STATUS_SUCCESS)
    {
        return error;
    }

    /* Command CTx switches. */
    error = BCC_CommandSwitches(drvConfig, cid, BCC_SWITCH_SEL_CT, odd, even);
    if (error != BCC_STATUS_SUCCESS)
    {
        return error;
    }

    /* Wait for 5 times diagnostic time constant. */
    BCC_WaitUs(5U * drvConfig->drvData.tauDiagn);

    /* Initiate conversion. */
    error = BCC_RunCOD(drvConfig, cid);
    if (error != BCC_STATUS_SUCCESS)
    {
        return error;
    }

    /* Read OV & UV flags. */
    error = BCC_ReadRegisters(drvConfig, cid, BCC_REG_CELL_OV_FLT_ADDR, 2U, fault);
    (*fltOvrv) = fault[0];
    (*fltUndv) = fault[1];

    return error;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_DiagCtxopenPart
 * Description   : This function is part of CTX open detect. It clear OV and UV
**                 fault registers, commands CT switches and measures voltage on
**                 cells.
 *
 *END**************************************************************************/
static bcc_status_t BCC_DiagCtxopenPart(bcc_drv_config_t* const drvConfig,
    bcc_cid_t cid, bcc_diag_switch_pos_t odd, bcc_diag_switch_pos_t even,
    uint16_t *measVal)
{
    bcc_status_t error;

    /* Clear OV, UV faults. */
    error = BCC_WriteRegister(drvConfig, cid, BCC_REG_CELL_OV_FLT_ADDR, 0x0000U);
    error |= BCC_WriteRegister(drvConfig, cid, BCC_REG_CELL_UV_FLT_ADDR, 0x0000U);
    if (error != BCC_STATUS_SUCCESS)
    {
        return error;
    }

    /* Command fault detection switches. */
    error = BCC_CommandSwitches(drvConfig, cid, BCC_SWITCH_SEL_CT, odd, even);
    if (error != BCC_STATUS_SUCCESS)
    {
        return error;
    }

    /* Wait for 5 times diagnostic time constant. */
    BCC_WaitUs(5U * drvConfig->drvData.tauDiag);

    /* Initiate conversion. */
    if ((error = BCC_RunCOD(drvConfig, cid)) != BCC_STATUS_SUCCESS)
    {
        return error;
    }

    /* Read conversion results. */
    if (drvConfig->device[cid - 1] == BCC_DEVICE_MC33771)
    {
        return BCC_ReadRegisters(drvConfig, cid, BCC_REG_MEAS_CELLX_ADDR_START_MC33771,
                    BCC_MAX_CELLS_MC33771, measVal);
    }
    else
    	return BCC_STATUS_PARAM_RANGE;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_DiagRunImeas
 * Description   : This function is part of current measurement diagnostics
 *                 (the first procedure). It resets Coulomb counter, initiates
 *                 conversion and reads measured current.
 *
 *END**************************************************************************/
static bcc_status_t BCC_DiagRunImeas(bcc_drv_config_t* const drvConfig,
    bcc_cid_t cid, int32_t *current)
{
    uint16_t measCurrentRaw[2];  /* Value of MEAS_ISENSE 1 and 2 registers */
    bcc_status_t error;

    /* 7a. Write ADC_CFG[CC_RST] = 1 to reset the coulomb counter COULOMB_CNT. */
    /* Reset Coulomb counter. */
    error = BCC_UpdateRegister(drvConfig, cid, BCC_REG_ADC_CFG_ADDR, BCC_W_CC_RST_MASK, BCC_CC_RESET);
    if (error != BCC_STATUS_SUCCESS)
    {
        return error;
    }

    /* 7b. Initiate conversion. */
    error = BCC_RunCOD(drvConfig, cid);
    if (error != BCC_STATUS_SUCCESS)
    {
        return error;
    }

    /* 8. Read conversion results. */
    error = BCC_ReadRegisters(drvConfig, cid, BCC_REG_MEAS_ISENSE1_ADDR, 2U, measCurrentRaw);

    /* Extracts current from measured raw value. */
    (*current) = BCC_GET_ISENSE_VOLT(BCC_GET_ISENSE_RAW(measCurrentRaw[0], measCurrentRaw[1]));

    return error;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_GpioOtUtPart
 * Description   : This function is part of GPIO OT and UT functional
 *                 verification.
 *
 *END**************************************************************************/
static bcc_status_t BCC_GpioOtUtPart(bcc_drv_config_t* const drvConfig,
    bcc_cid_t cid, uint16_t gpioCFG2Val, uint16_t *otUtStat)
{
    bcc_status_t error;

    /* 3. Set GPIO_CFG2[GPIOx_DR] register to drive output for overtemperature
     * or undertemperature. */
    error = BCC_WriteRegister(drvConfig, cid, BCC_REG_GPIO_CFG2_ADDR, gpioCFG2Val);
    if (error != BCC_STATUS_SUCCESS)
    {
        return error;
    }

    /* 5. Initiate conversion. */
    error = BCC_RunCOD(drvConfig, cid);
    if (error != BCC_STATUS_SUCCESS)
    {
        return error;
    }

    /* 6. Evaluate / Publish results.
    * Conversion data < TH_ANx_OT -> OT (ANx_OT) and > TH_ANx_UT -> UT (ANx_UT). */
    error = BCC_ReadRegisters(drvConfig, cid, BCC_REG_AN_OT_UT_FLT_ADDR, 1U, otUtStat);
    if (error != BCC_STATUS_SUCCESS)
    {
        return error;
    }

    /* Clear OT/UT faults before exit. */
    return BCC_WriteRegister(drvConfig, cid, BCC_REG_AN_OT_UT_FLT_ADDR, 0x0000U);
}

/******************************************************************************
 * API
 ******************************************************************************/

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_Init
 * Description   : This function initializes the Battery Cell Controller
 *                 device(s), configures its registers, assigns CID and
 *                 initializes internal driver data.
 *
 *END**************************************************************************/
bcc_status_t BCC_Init(bcc_drv_config_t* const drvConfig,
    const uint16_t devConf[][BCC_INIT_CONF_REG_CNT])
{
    bcc_status_t error;
    uint8_t dev;
    uint8_t cell;
    uint8_t cid;

    uint16_t tempCnt = 0;

    if ((drvConfig->devicesCnt == 0) ||(drvConfig->devicesCnt > ((drvConfig->commMode == BCC_MODE_SPI) ? BCC_DEVICE_CNT_MAX_SPI : BCC_DEVICE_CNT_MAX_TPL)))
    {
        return BCC_STATUS_PARAM_RANGE;
    }
    /*calculate constant τ for Diag safety mechanism 1 and 2*/
    if (drvConfig->compConfig.ctFilterComp != NULL)
    {
        BCC_CalcTauDiag(drvConfig);
    }
    /*mask un used cells*/
    for (dev = 0; dev < drvConfig->devicesCnt; dev++)
    {
		drvConfig->drvData.cellMap[dev] = BCC_CM_MC33771_7CELLS;
		for (cell = BCC_MIN_CELLS_MC33771; cell < drvConfig->cellCnt[dev]; cell++)
		{
			drvConfig->drvData.cellMap[dev] |= (0x0400U >> (cell - BCC_MIN_CELLS_MC33771));
		}
    }

    /* Initialize TAG ID. */
    for (cid = 0; cid < drvConfig->devicesCnt; cid++)
    {
        drvConfig->drvData.rcTbl[cid] = 0U;
    }

    /* RESET -> 0, if SPI mode used */
    BCC_WriteRstPin(drvConfig->drvInstance, 0);

    if (drvConfig->commMode == BCC_MODE_TPL)
    {
        /* EN -> 0 */
        BCC_WriteEnPin(drvConfig->drvInstance, 0);
    }

    /* Wake-up BCC (in case of IDLE mode). */
    if ((error = BCC_WakeUp(drvConfig)) != BCC_STATUS_SUCCESS)
    {
        return error;
    }

    /* Reset (soft reset) BCC to assign CID (in case of CID was already assigned).*/
    (void)BCC_SoftwareReset(drvConfig);

    /* Wait for 5 ms (tvpwr(ready)) - for the IC to be ready for initialization. */
//    while (tempCnt++ <= 1000)
    {
    	BCC_WaitUs(5000U);//BCC_WaitMs(5U);
    }

    return BCC_InitDevices(drvConfig, devConf);
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_WriteRegister
 * Description   : This function writes a value to addressed register of
 *                 selected Battery Cell Controller device.
 *
 *END**************************************************************************/
bcc_status_t BCC_WriteRegister(bcc_drv_config_t* const drvConfig, bcc_cid_t cid, uint8_t regAddr, uint16_t regVal)
{

    if (drvConfig->commMode == BCC_MODE_SPI)
    {
        return BCC_WriteRegisterSpi(drvConfig, cid, regAddr, regVal);
    }
    else
    {
        return BCC_WriteRegisterTpl(drvConfig, cid, regAddr, regVal);
    }
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_WriteRegisterGlobal
 * Description   : This function writes a value to addressed register of all
 *                 configured BCC devices. Intended for TPL mode only.
 *
 *END**************************************************************************/
bcc_status_t BCC_WriteRegisterGlobal(bcc_drv_config_t* const drvConfig,
     uint8_t regAddr, uint16_t regVal)
{

    return BCC_WriteRegisterGlobalTpl(drvConfig, regAddr, regVal);
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_ReadRegisters
 * Description   : This function reads a value from addressed register (or desired
 *                 number of registers) of selected Battery Cell Controller device.
 *
 *END**************************************************************************/
bcc_status_t BCC_ReadRegisters(bcc_drv_config_t* const drvConfig, bcc_cid_t cid,
    uint8_t regAddr, uint8_t regCnt, uint16_t* regVal)
{
    uint8_t curRegCnt = regCnt;    /* Current number of registers to read. */
    bcc_status_t error;

    if (drvConfig->commMode == BCC_MODE_SPI)
    {
        return BCC_ReadRegistersSpi(drvConfig, cid, regAddr, regCnt, regVal);
    }
    else
    {
		error = BCC_ReadRegistersTpl(drvConfig, cid, regAddr, curRegCnt, regVal);
		if (error != BCC_STATUS_SUCCESS)
		{
			return error;
		}
    }

    return BCC_STATUS_SUCCESS;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_UpdateRegister
 * Description   : This function updates content of a selected register. It
 *                 affects bits specified by a bit mask only.
 *
 *END**************************************************************************/
bcc_status_t BCC_UpdateRegister(bcc_drv_config_t* const drvConfig,
    bcc_cid_t cid, uint8_t regAddr, uint16_t regMask, uint16_t regVal)
{
    uint16_t regValTemp;
    bcc_status_t error;

    if (cid > drvConfig->devicesCnt)
    {
        return BCC_STATUS_PARAM_RANGE;
    }

    error = BCC_ReadRegisters(drvConfig, cid, regAddr, 1U, &regValTemp);
    if (error != BCC_STATUS_SUCCESS)
    {
        return error;
    }

    /* Update register value. */
    regValTemp = BCC_REG_UNSET_BIT_VALUE(regValTemp, regMask);
    regValTemp = BCC_REG_SET_BIT_VALUE(regValTemp, (regVal & regMask));

    return BCC_WriteRegister(drvConfig, cid, regAddr, regValTemp);
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_Sleep
 * Description   : This function sets sleep mode to all Battery Cell Controller
 *                 devices.
 *
 *END**************************************************************************/
bcc_status_t BCC_Sleep(bcc_drv_config_t* const drvConfig)
{
    bcc_status_t error;

    if (drvConfig->commMode == BCC_MODE_SPI)
    {
        return BCC_WriteRegister(drvConfig, BCC_CID_DEV1, BCC_REG_SYS_CFG_GLOBAL_ADDR, BCC_GO2SLEEP_ENABLED);
    }
    else
    {
        error = BCC_WriteRegisterGlobal(drvConfig, BCC_REG_SYS_CFG_GLOBAL_ADDR,
                    BCC_GO2SLEEP_ENABLED);

        /* Set MC33664 sleep mode. */
        BCC_WriteEnPin(drvConfig->drvInstance, 0);

        return error;
    }
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_WakeUp
 * Description   : This function sets normal mode to all Battery Cell Controller
 *                 devices.
 *
 *END**************************************************************************/
bcc_status_t BCC_WakeUp(bcc_drv_config_t *drvConfig)
{
    bcc_status_t error;

    BCC_CS2GPIO();
    //test
    //PINS_DRV_SetPinDirection(PTA,1<<9,1);
    if (drvConfig->commMode == BCC_MODE_SPI)
    {
        /* A transition of CSB from low to high. */
        /* CSB_TX low. */
        BCC_WriteCsbPin(drvConfig->drvInstance, 0);
        /* Wait for t1; 20 us < t1. */
        BCC_WaitUs(20U);

        /* CSB_TX high. */
        BCC_WriteCsbPin(drvConfig->drvInstance, 1);
        /* Wait for t2; 500 us < t2. */
        BCC_WaitUs(600U);
    }
    /*TPL mode*/
    else
    {
        /* Enable TPL device. */
        if ((error = BCC_EnableTPL(drvConfig)) != BCC_STATUS_SUCCESS)
        {
            return error;
        }
        __asm volatile ("cpsid i" : : : "memory");//taskDISABLE_INTERRUPTS();
        /* Two consecutive transitions of CSB_TX from low to high. */
        /* CSB_TX low. */
        BCC_WriteCsbPin(drvConfig->drvInstance, 0);
        /* Wait for t1; 20 us < t1 < 24us. */
        //taskDISABLE_INTERRUPTS();
        BCC_WaitUs(22U);
        //taskENABLE_INTERRUPTS();
        /* CSB_TX high. */
        BCC_WriteCsbPin(drvConfig->drvInstance, 1);
        /* Wait for t2; 500 us < t2 < 700μs. */
        //taskDISABLE_INTERRUPTS();
        BCC_WaitUs(600U);
        //taskENABLE_INTERRUPTS();
        /* CSB_TX low. */
        BCC_WriteCsbPin(drvConfig->drvInstance, 0);
        /* Wait for t1; 20 us < t1. */
        //taskDISABLE_INTERRUPTS();
        BCC_WaitUs(22U);
        //taskENABLE_INTERRUPTS();
        /* CSB_TX high. */
        BCC_WriteCsbPin(drvConfig->drvInstance, 1);
        //        BCC_GPIO2CS();
        /*Sleep mode to normal mode time after TPL bus wake-up: 1ms*/
        BCC_WaitUs(1200);

        __asm volatile ("cpsie i" : : : "memory");//taskENABLE_INTERRUPTS();
    }
//    BCC_GPIO2CS();
    return BCC_STATUS_SUCCESS;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_StartConversion
 * Description   : This function starts ADC conversion. It sets Start of
 *                 Conversion bit and new value of TAG ID in ADC_CFG register.
 *
 *END**************************************************************************/
bcc_status_t BCC_StartConversion(bcc_drv_config_t* const drvConfig, uint16_t ADC_CFG)
{
    uint16_t regVal;     /* Value of ADC_CFG register. */

    regVal = ADC_CFG;
    /* Increment TAG ID (4 bit value). */
//    drvConfig->drvData.tagId = (drvConfig->drvData.tagId + 1U) & 0x0FU;

    /* Set new TAG ID and Start of Conversion bit. */
    regVal = BCC_REG_SET_BIT_VALUE(regVal, BCC_W_SOC_MASK);

    if(drvConfig->commMode == BCC_MODE_TPL)
        return BCC_WriteRegisterGlobal(drvConfig, BCC_REG_ADC_CFG_ADDR, regVal);

    else if (drvConfig->commMode == BCC_MODE_SPI)
    	return BCC_WriteRegister(drvConfig, BCC_CID_DEV1, BCC_REG_ADC_CFG_ADDR, regVal);
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_IsConverting
 * Description   : This function checks status of conversion defined by End of
 *                 Conversion bit in ADC_CFG register.
 *
 *END**************************************************************************/
bcc_status_t BCC_IsConverting(bcc_drv_config_t* const drvConfig, bcc_cid_t cid,
    bool* completed)
{
    uint16_t regVal;     /* Value of ADC_CFG register. */
    bcc_status_t error;

    if ((cid == BCC_CID_UNASSIG) || (cid > drvConfig->devicesCnt))
    {
        return BCC_STATUS_PARAM_RANGE;
    }

    error = BCC_ReadRegisters(drvConfig, cid, BCC_REG_ADC_CFG_ADDR, 1U, &regVal);

    regVal = regVal & BCC_R_EOC_N_MASK;
    *(completed) = (regVal == 0x00U);

    return error;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_GetRawMeasurements
 * Description   : This function reads the measurement registers and returns raw
 *                 values. You can use macros defined in BCC header file to
 *                 perform correct unit conversion.
 *
 *END**************************************************************************/
bcc_status_t BCC_GetRawMeasurements(bcc_drv_config_t* const drvConfig, bcc_cid_t cid)
{
    bcc_status_t error;
    uint8_t i;
    uint16_t measurements[30];

    if ((cid == BCC_CID_UNASSIG) || (cid > drvConfig->devicesCnt)||(drvConfig->device[cid - 1] != BCC_DEVICE_MC33771))
    {
        return BCC_STATUS_PARAM_RANGE;
    }

    error = BCC_ReadRegisters(drvConfig, cid, 0x33/*BCC_REG_CC_NB_SAMPLES_ADDR*/, 14/*BCC_MEAS_CNT*/, &measurements[6]);

    if(error!=BCC_STATUS_SUCCESS)
    	return error;

    /* Mask the other registers (starting at 5th register). */
    for (i = 5U; i < BCC_MEAS_CNT-10; i++)
    {
        BatPack.u16RawCell[cid][i-5] =(measurements[i] & BCC_R_MEAS_MASK);
    }

    error = BCC_ReadRegisters(drvConfig, cid, BCC_REG_MEAS_IC_TEMP_ADDR, 1/*BCC_MEAS_CNT*/, &measurements[22]);  //¶ÁÐ¾Æ¬ÎÂ¶È
	if(error== BCC_STATUS_SUCCESS)
	{
		/* Mask the other registers (starting at 5th register). */
		for (i = 22U; i < 23; i++)
		{
				BatPack.u16RawCell[cid][i] =(measurements[i] & BCC_R_MEAS_MASK);
		}
	}

    return error;
}



void BCC_DecodeRawMeasurements(bcc_cid_t cid)
{
	uint8_t idx = 0;

	for(idx = 1; idx<15; idx++){//25
		BatPack.u16VoltCell[cid][idx] = BCC_GET_VOLT(BatPack.u16RawCell[cid][idx]);
	}

	BatPack.u16Temp[cid] = BCC_GET_IC_TEMP(BatPack.u16RawCell[cid][22]);
}

/* Function: calculate mean of ADC data , moving-average */
void BCC1_calc_Mean16(uint8_t cid, uint16_t BufPtrIN[], uint16_t BufPtrOUT[], uint16_t len)
{
#define AVERAGE_CNT 6
	uint32_t i=0, j=0;
	uint32_t sum;
	static uint32_t cnt[16];
	static uint32_t index[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
	static uint32_t buffer[16][AVERAGE_CNT][BCC_MEAS_CNT];

	cid -= 1;

	if(cnt[cid] < AVERAGE_CNT)
		cnt[cid]++;

	for(i =0; i < len; i++)
		buffer[cid][index[cid]][i] = BufPtrIN[i];

	for(j = 0; j < len; j++)
	{
		sum = 0;
		for(i = 0; i< cnt[cid]; i++){
			sum += buffer[cid][i][j];
			BufPtrOUT[j] = sum / cnt[cid];
		}
	}

	if(index[cid]++ >= (AVERAGE_CNT -1))
		index[cid] = 0;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_GetFaultStatus
 * Description   : This function reads the status registers and returns raw
 *                 values. You can use constants defined in bcc_mc3377x.h file.
 *
 *END**************************************************************************/
bcc_status_t BCC_GetFaultStatus(bcc_drv_config_t* const drvConfig,
    bcc_cid_t cid, uint16_t status[][11])
{
    bcc_status_t error;
    uint8_t FaultAddr[10]={
    		BCC_REG_CELL_OV_FLT_ADDR, BCC_REG_CELL_UV_FLT_ADDR,BCC_REG_CB_OPEN_FLT_ADDR,
			BCC_REG_CB_SHORT_FLT_ADDR,BCC_REG_AN_OT_UT_FLT_ADDR,BCC_REG_GPIO_SHORT_ADDR,
			BCC_REG_COM_STATUS_ADDR,BCC_REG_FAULT1_STATUS_ADDR,
			BCC_REG_FAULT2_STATUS_ADDR,BCC_REG_FAULT3_STATUS_ADDR
    };
    uint8_t i;

    if ((cid == BCC_CID_UNASSIG) || (cid > drvConfig->devicesCnt))
    {
        return BCC_STATUS_PARAM_RANGE;
    }
    for(i = 0; i<10;i++){
    	error = BCC_ReadRegisters(drvConfig, cid, FaultAddr[i], 1U, &status[cid][i]);
    	if(error)
    		break;
    }
    return error;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_ClearFaultStatus
 * Description   : This function clears selected fault status register.
 *
 *END**************************************************************************/
bcc_status_t BCC_ClearFaultStatus(bcc_drv_config_t* const drvConfig, bcc_cid_t cid)
{
    /* This array is intended for conversion of bcc_fault_status_t value to
     * a BCC register address. */
	bcc_status_t error;
	uint8_t regidx;
    const uint8_t REG_ADDR_MAP[BCC_STAT_CNT] = {
        BCC_REG_CELL_OV_FLT_ADDR, BCC_REG_CELL_UV_FLT_ADDR,
        BCC_REG_CB_OPEN_FLT_ADDR, BCC_REG_CB_SHORT_FLT_ADDR,
        BCC_REG_AN_OT_UT_FLT_ADDR,BCC_REG_GPIO_SHORT_ADDR,
         BCC_REG_COM_STATUS_ADDR,BCC_REG_FAULT1_STATUS_ADDR,
         BCC_REG_FAULT2_STATUS_ADDR,BCC_REG_FAULT3_STATUS_ADDR
    };

    if ((cid == BCC_CID_UNASSIG) || (cid > drvConfig->devicesCnt))
    {
        return BCC_STATUS_PARAM_RANGE;
    }

    for(regidx = 0; regidx <10; regidx++){
    	error = BCC_WriteRegister(drvConfig, cid, REG_ADDR_MAP[regidx], 0x00U);
    	if(error!=BCC_STATUS_SUCCESS)
    		return error;
    }

    return BCC_STATUS_SUCCESS;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_SoftwareReset
 * Description   : This function resets BCC device using software reset. It
 *                 enters reset via SPI or TPL interface.
 *
 *END**************************************************************************/
bcc_status_t BCC_SoftwareReset(bcc_drv_config_t* const drvConfig)
{
    bcc_status_t error;

    /* Note: it is not necessary to read content of SYS_CFG1 register
    * to change only RST bit, because registers are set to default values. */

    if (drvConfig->commMode == BCC_MODE_TPL)
    {
        /* TPL Global reset command. */
        error = BCC_WriteRegisterGlobal(drvConfig, BCC_REG_SYS_CFG1_ADDR, BCC_W_SOFT_RST_MASK);
    }
    else
    {
        error = BCC_WriteRegister(drvConfig, BCC_CID_DEV1, BCC_REG_SYS_CFG1_ADDR, BCC_W_SOFT_RST_MASK);
    }

    return error;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_HardwareReset
 * Description   : This function resets BCC device using GPIO pin.
 *
 *END**************************************************************************/
void BCC_HardwareReset(bcc_drv_config_t* const drvConfig)
{
    BCC_WriteRstPin(drvConfig->drvInstance, 1);
    /* Wait at least tRESETFLT (100 us). */
    BCC_WaitUs(100);
    BCC_WriteRstPin(drvConfig->drvInstance, 0);
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_SetGPIOOutput
 * Description   : This function sets output value of one BCC GPIO pin.
 *
 *END**************************************************************************/
bcc_status_t BCC_SetGPIOOutput(bcc_drv_config_t* const drvConfig, uint8_t cid,
        uint8_t gpioSel, bool val)
{
    uint16_t regVal;    /* Value of GPIO_CFG2 register. */
    bcc_status_t error;

    if ((cid == BCC_CID_UNASSIG) || (cid > drvConfig->devicesCnt) ||
        (gpioSel >= BCC_GPIO_INPUT_CNT))
    {
        return BCC_STATUS_PARAM_RANGE;
    }

    /* Read and update content of GPIO_CFG2 register. */
    error = BCC_ReadRegisters(drvConfig, cid, BCC_REG_GPIO_CFG2_ADDR, 1U, &regVal);
    if (error != BCC_STATUS_SUCCESS)
    {
        return error;
    }

    /* Set GPIO output value. */
    regVal &= ~((uint16_t)BCC_RW_GPIOX_DR_MASK(gpioSel));
    regVal |= (val) ? BCC_GPIOx_HIGH(gpioSel) : BCC_GPIOx_LOW(gpioSel);

    return BCC_WriteRegister(drvConfig, cid, BCC_REG_GPIO_CFG2_ADDR, regVal);
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_SetCBIndividually
 * Description   : This function sets state of individual cell balancing driver.
 *
 *END**************************************************************************/
bcc_status_t BCC_SetCBIndividually(bcc_drv_config_t* const drvConfig,
    uint8_t cid, uint8_t cellIndex, bool enable, uint16_t timer)
{
    bcc_status_t error = BCC_STATUS_SUCCESS;
    uint16_t regVal;

    if ((cid == BCC_CID_UNASSIG) || (cid > drvConfig->devicesCnt))
    {
        return BCC_STATUS_PARAM_RANGE;
    }

    if (cellIndex > BCC_MAX_CELLS_MC33771)
    {
        return BCC_STATUS_PARAM_RANGE;
    }
    /*maximum timer is 511 min*/
    if(timer > 0x1FF)
    {
    	timer = 0x1FF;
    }

    if (enable)
    {
        if (BCC_IS_CELL_CONN(drvConfig, cid, cellIndex + 1U))
        {
        	regVal = BCC_SET_CB_TIMER(regVal, timer);
        	regVal|= BCC_CB_ENABLED;
            error = BCC_WriteRegister(drvConfig, cid, BCC_REG_CB1_CFG_ADDR + cellIndex - 1,regVal);
        }
    }
    else
    {
        /* Disable individual CB driver with use of CBx_CFG[CB_EN]. */
        error = BCC_UpdateRegister(drvConfig, cid, BCC_REG_CB1_CFG_ADDR + cellIndex - 1,
                    BCC_W_CB_EN_MASK, BCC_CB_DISABLED);
    }

    return error;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_GetCBStatus
 * Description   : This function checks status cell balance by reading
 *                 CB_DRV_STS register.
 *
 *END**************************************************************************/
bcc_status_t BCC_GetCBStatus(bcc_drv_config_t* const drvConfig, bcc_cid_t cid,
		uint16_t *status)
{
    uint16_t regVal;     /* Value of ADC_CFG register. */
    bcc_status_t error;

    if ((cid == BCC_CID_UNASSIG) || (cid > drvConfig->devicesCnt))
    {
        return BCC_STATUS_PARAM_RANGE;
    }

    error = BCC_ReadRegisters(drvConfig, cid, BCC_REG_CB_DRV_STS_ADDR, 1U, &regVal);

    *(status) = regVal;

    return error;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_GetNtcCelsius
 * Description   : This function calculates temperature from raw value of
 *                 MEAS_ANx register.
 *
 *END**************************************************************************/
bcc_status_t BCC_GetNtcCelsius(bcc_drv_config_t* const drvConfig,
    uint16_t regVal, int16_t* temp)
{
    int16_t left = 0;    /* Pointer (index) to the left border of interval (NTC table). */
    /* Pointer (index) to the right border of interval (NTC table). */
    int16_t right = BCC_NTC_TABLE_SIZE - 1;
    int16_t middle;      /* Pointer (index) to the middle of interval (NTC table). */
    int8_t degTenths;    /* Fractional part of temperature value. */

    /* Check range of NTC table. */
    if (g_ntcTable[BCC_NTC_TABLE_SIZE - 1] > regVal)
    {
        *temp = BCC_COMP_TEMP(BCC_NTC_TABLE_SIZE - 1, 0);
        return BCC_STATUS_PARAM_RANGE;
    }
    if (g_ntcTable[0] < regVal)
    {
        *temp = BCC_COMP_TEMP(0, 0);
        return BCC_STATUS_PARAM_RANGE;
    }

    regVal &= BCC_GET_MEAS_RAW(regVal);

    /* Search for an array item which is close to the register value provided
    * by user (regVal). Used method is binary search in sorted array. */
    while ((left + 1) != right)
    {
        /* Split interval into halves. */
        middle = (left + right) >> 1U;
        if (g_ntcTable[middle] <= regVal)
        {
            /* Select right half (array items are in descending order). */
            right = middle;
        }
        else
        {
            /* Select left half. */
            left = middle;
        }
    }

    /* Notes: found table item (left) is less than the following item in the
    * table (left + 1).
    * The last item cannot be found (algorithm property). */

    /* Calculate fractional part of temperature. */
    degTenths = (g_ntcTable[left] - regVal) / ((g_ntcTable[left] - g_ntcTable[left + 1]) / 10);
    (*temp) = BCC_COMP_TEMP(left, degTenths);

    return BCC_STATUS_SUCCESS;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_PauseCBDrivers
 * Description   : This function can be used to manual pause cell balancing before
 *                 on demand conversion.
 *
 *END**************************************************************************/
bcc_status_t BCC_PauseCBDrivers(bcc_drv_config_t* const drvConfig, bcc_cid_t cid, bool pause)
{
    uint16_t regVal = (pause) ? BCC_CB_MAN_PAUSE_ENABLED : BCC_CB_MAN_PAUSE_DISABLED;

    if ((cid == BCC_CID_UNASSIG) || (cid > drvConfig->devicesCnt))
    {
        return BCC_STATUS_PARAM_RANGE;
    }

    return BCC_UpdateRegister(drvConfig, cid, BCC_REG_SYS_CFG1_ADDR,
                BCC_RW_CB_MANUAL_PAUSE_MASK, regVal);
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_CheckCid
 * Description   : This function read INIT register of NO.cid device to
 *                 ensure NO.cid device communication status normally.
 *
 *END**************************************************************************/

bcc_status_t BCC_CheckCid(bcc_drv_config_t* const drvConfig, bcc_cid_t cid)
{
    uint16_t regVal = 0U;   /* Value of a register. */
    bcc_status_t error = BCC_STATUS_SUCCESS;

    /* Check if assigned node replies. */
    error = BCC_ReadRegisters(drvConfig, cid, BCC_REG_INIT_ADDR, 1U, &regVal);

    return error;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_GetCtrlRegMap
 * Description   : This function read some configurable register of NO.cid device.
 *
 *END**************************************************************************/
bcc_status_t BCC_GetCtrlRegMap(bcc_drv_config_t* const drvConfig, bcc_cid_t cid,
		uint16_t StatusPtr[][31], uint8_t *AddrMap)
{

	bcc_status_t error = BCC_STATUS_SUCCESS;
    uint8_t index = 0;
    uint8_t RegMap[31] ={
    		BCC_REG_INIT_ADDR, BCC_REG_SYS_CFG_GLOBAL_ADDR, BCC_REG_SYS_CFG1_ADDR,
			BCC_REG_SYS_CFG2_ADDR, BCC_REG_SYS_DIAG_ADDR, BCC_REG_ADC_CFG_ADDR,
			BCC_REG_OV_UV_EN_ADDR, BCC_REG_GPIO_CFG1_ADDR, BCC_REG_GPIO_CFG2_ADDR,
			BCC_REG_FAULT_MASK1_ADDR, BCC_REG_FAULT_MASK2_ADDR, BCC_REG_FAULT_MASK3_ADDR,
			BCC_REG_WAKEUP_MASK1_ADDR, BCC_REG_WAKEUP_MASK2_ADDR, BCC_REG_WAKEUP_MASK3_ADDR,
			BCC_REG_TPL_CFG_ADDR,BCC_REG_ADC2_OFFSET_COMP_ADDR, BCC_REG_CB1_CFG_ADDR, BCC_REG_CB2_CFG_ADDR,
			BCC_REG_CB3_CFG_ADDR, BCC_REG_CB4_CFG_ADDR, BCC_REG_CB5_CFG_ADDR,
			BCC_REG_CB6_CFG_ADDR, BCC_REG_CB7_CFG_ADDR, BCC_REG_CB8_CFG_ADDR,
			BCC_REG_CB9_CFG_ADDR, BCC_REG_CB10_CFG_ADDR, BCC_REG_CB11_CFG_ADDR,
			BCC_REG_CB12_CFG_ADDR, BCC_REG_CB13_CFG_ADDR, BCC_REG_CB14_CFG_ADDR,

    };

    if (StatusPtr == NULL) {
        return BCC_STATUS_PARAM_RANGE;
    }

    for(index = 0; index <31; index++){
    	AddrMap[index] = RegMap[index];
    	error = BCC_ReadRegisters(drvConfig, cid, RegMap[index], 1U, &StatusPtr[cid-1][index]);
    	if(error!= BCC_STATUS_SUCCESS)
    		return error;
    }

    return error;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_GetCtrlRegMap
 * Description   : This function read some status register of NO.cid device.
 *
 *END**************************************************************************/
bcc_status_t BCC_GetSTSRegMap(bcc_drv_config_t* const drvConfig, bcc_cid_t cid,
		uint16_t StatusPtr[][15], uint8_t *AddrMap)
{
	bcc_status_t error = BCC_STATUS_SUCCESS;
    uint8_t index = 0;
    uint8_t RegMap[15] ={
    		BCC_REG_CELL_OV_FLT_ADDR, BCC_REG_CELL_UV_FLT_ADDR, BCC_REG_CB_OPEN_FLT_ADDR,
			BCC_REG_CB_SHORT_FLT_ADDR, BCC_REG_CB_DRV_STS_ADDR, BCC_REG_GPIO_STS_ADDR,
			BCC_REG_AN_OT_UT_FLT_ADDR, BCC_REG_GPIO_SHORT_ADDR, BCC_REG_I_STATUS_ADDR,
			BCC_REG_COM_STATUS_ADDR, BCC_REG_FAULT1_STATUS_ADDR, BCC_REG_FAULT2_STATUS_ADDR,
			BCC_REG_FAULT3_STATUS_ADDR, BCC_REG_MEAS_ISENSE2_ADDR, BCC_REG_SILICON_REV_ADDR,
    };
    uint16_t regtemp = 0;

    if (StatusPtr == NULL) {
        return BCC_STATUS_PARAM_RANGE;
    }

    for(index = 0; index <15; index++){
    	AddrMap[index] = RegMap[index];
    	error = BCC_ReadRegisters(drvConfig, cid, RegMap[index], 1U, &StatusPtr[cid-1][index]);
    	if(error!= BCC_STATUS_SUCCESS)
    		return error;
    }

    return error;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : BCC_Configfuse_I
 * Description   : This function provide example to calibrate I_PGA by change
 *                 fuse data.
 *END**************************************************************************/
bcc_status_t BCC_Configfuse_I(bcc_drv_config_t* const drvConfig, bcc_cid_t cid, uint8_t cali_data)
{

	uint16_t Regval = 0, idx = 0;
	uint16_t fusedata[24], fusedata1[24],fusedata2[24];
	uint16_t temp;
	bcc_status_t Error = BCC_STATUS_SUCCESS;

	/*1. set SYS_CFG2[HAMMENCOD] bit 1*/
	Error = BCC_UpdateRegister(drvConfig, cid, BCC_REG_SYS_CFG2_ADDR, BCC_RW_HAMM_ENCOD_MASK, BCC_HAMM_ENCOD_ENABLED);
	if(Error != BCC_STATUS_SUCCESS)
		return Error;

	/*2. Read all data from fuse mirror*/
	for(idx = 0; idx<=23; idx++){
		Regval = ((idx<<BCC_RW_FMR_ADDR_SHIFT)|BCC_FSTM_WRITE_DIS);
		Error = BCC_WriteRegister(drvConfig, cid, BCC_REG_FUSE_MIRROR_CTRL_ADDR,Regval);
		Error |= BCC_ReadRegisters(drvConfig, cid, BCC_REG_FUSE_MIRROR_DATA_ADDR, 1U, &fusedata[idx]);
		if(Error != BCC_STATUS_SUCCESS)
			return Error;
	}

	/*2. Write all data into fuse mirror*/
	Regval = ((0<<BCC_RW_FMR_ADDR_SHIFT)|BCC_FSTM_WRITE_EN);
	Error = BCC_WriteRegister(drvConfig, 1, BCC_REG_FUSE_MIRROR_CTRL_ADDR,Regval);
	for(idx = 0; idx<=21; idx++){
		/*calculate new fuse_data for I_PGA*/
		if((idx>=17)&&(idx<=20)){
			temp = fusedata[idx]&0x1FF;
			temp = (temp + cali_data)&0x1FF;
			fusedata1[idx] = (fusedata[idx]&0xFE00)|temp;
		}
		else
			fusedata1[idx] = fusedata[idx];
		/*2. Write into fuse mirror*/
		Regval = ((idx<<BCC_RW_FMR_ADDR_SHIFT)|BCC_FSTM_WRITE_EN);
		Error = BCC_WriteRegister(drvConfig,cid, BCC_REG_FUSE_MIRROR_CTRL_ADDR,Regval);
		Error |= BCC_WriteRegister(drvConfig, cid, BCC_REG_FUSE_MIRROR_DATA_ADDR, fusedata1[idx]);
		if(Error != BCC_STATUS_SUCCESS)
			return Error;
	}
	Delayus(1000);
	/*3. Read DED_ENCODE1 and DED_ENCODE2 */
	Error = BCC_ReadRegisters(drvConfig, cid, BCC_REG_DED_ENCODE1_ADDR, 1U, &fusedata1[22]);
	Error |= BCC_ReadRegisters(drvConfig, cid, BCC_REG_DED_ENCODE2_ADDR, 1U, &fusedata1[23]);
	/*4. Write new DED_ENCODE1 and DED_ENCODE2 into fuse mirror*/
	Regval = ((22<<BCC_RW_FMR_ADDR_SHIFT)|BCC_FSTM_WRITE_EN);
	Error |= BCC_WriteRegister(drvConfig,cid, BCC_REG_FUSE_MIRROR_CTRL_ADDR,Regval);
	Delayus(10);
	Error |= BCC_WriteRegister(drvConfig, cid, BCC_REG_FUSE_MIRROR_DATA_ADDR, fusedata1[23]);

	Regval = ((23<<0x08)|(1<<0x03));
	Error |= BCC_WriteRegister(drvConfig,1, BCC_REG_FUSE_MIRROR_CTRL_ADDR,Regval);
	Delayus(10);
	Error |= BCC_WriteRegister(drvConfig, 1, BCC_REG_FUSE_MIRROR_DATA_ADDR, fusedata1[22]);

	Error |= BCC_WriteRegister(drvConfig,1, BCC_REG_FUSE_MIRROR_CTRL_ADDR,0x0C);
	if(Error != BCC_STATUS_SUCCESS)
		return Error;

	/*4. set SYS_CFG2[HAMMENCOD] bit 0*/
	Error = BCC_UpdateRegister(drvConfig, cid, BCC_REG_SYS_CFG2_ADDR, BCC_RW_HAMM_ENCOD_MASK, BCC_HAMM_ENCOD_DISABLED);

	return Error;
}
#endif
